package controller

import (
	"cn.pinming/gencode/core"
	"cn.pinming/gencode/core/api"
	"cn.pinming/gencode/core/controller"
	"cn.pinming/gencode/core/logs"
	"cn.pinming/gencode/core/utils"
	"cn.pinming/gencode/modules/gen/vo"
	"database/sql"
	"fmt"
	"github.com/kataras/iris/mvc"
	"strings"
	"sync"
)

type GencodeController struct {
	controller.BaseController
}

func (this *GencodeController) GetSetting() mvc.Result {
	return this.ViewResult("gen/setting")
}

func (this *GencodeController) PostSetting() mvc.Result {
	setting := new(vo.GenSetting)
	this.Ctx.ReadForm(setting)
	if setting.Port == 0 {
		setting.Port = 3306
	}
	url := core.FormatDburl(setting.Username, setting.Password, setting.Ip, setting.Port, setting.Database)
	this.Session.Set("dburl", url)
	return mvc.Response{
		Path: "tables",
	}
}

func (this *GencodeController) GetTables() mvc.Result {
	sett := this.Session.Get("dburl")
	dburl, ok := sett.(string)
	if !ok {
		logs.Error("配置信息无效")
		this.Data["msg"] = "配置信息无效"
		return this.ViewResult("gen/setting")
	}
	this.Data["dburl"] = dburl
	return this.ViewResult("gen/tables")
}

func (this *GencodeController) PostTables() interface{} {
	sett := this.Session.Get("dburl")
	dburl, _ := sett.(string)
	limit, _ := this.Ctx.PostValueInt("limit")
	page, _ := this.Ctx.PostValueInt("page")
	tableName := this.Ctx.PostValue("tableName")
	return this.searchData(limit, page, tableName, dburl)
}

func (this *GencodeController) PostProcess() api.Result {
	var data = new(processRequest)
	this.Ctx.ReadJSON(data)
	if data.List == nil || len(data.List) == 0 {
		return this.Error("请选择待处理的表")
	}
	sett := this.Session.Get("dburl")
	dburl, ok := sett.(string)
	if !ok {
		logs.Error("配置信息无效")
		this.Data["msg"] = "配置信息无效"
		return this.Error("数据连接字符串已经丢失")
	}
	go func() {
		waitGroup := sync.WaitGroup{}
		for index, tableName := range data.List {
			msg := fmt.Sprintf("开始处理，表名称:%s 索引:%d", tableName, index)
			logs.Debugf(msg)
			waitGroup.Add(1)
			go this.processColumnInfo(dburl, tableName, &waitGroup)
		}
		waitGroup.Wait()
		logs.Debugf("业务处理完毕,已经处理的表： %v", data.List)
	}()
	logs.Debugf("API接口返回")
	return this.Success()
}

func (this *GencodeController) searchData(limit, page int, tableName, dburl string) PagedTableList {
	tempDb, _ := sql.Open("mysql", dburl)
	defer tempDb.Close()
	sqlClause := `select table_name tableName, engine, table_comment tableComment from information_schema.tables where table_schema = (select database()) `
	hasArgs := len(tableName) > 0
	if hasArgs {
		tableName = "%" + tableName + "%"
		sqlClause = fmt.Sprintf("%s and table_name like ? ", sqlClause)
	}
	if limit < 1 {
		limit = 15
	}
	if page < 1 {
		page = 1
	}
	skip := limit * (page - 1)
	sqlClause = fmt.Sprintf("%s limit %d,%d", sqlClause, skip, limit)
	var rows *sql.Rows
	var err error
	if hasArgs {
		rows, err = tempDb.Query(sqlClause, tableName)
	} else {
		rows, err = tempDb.Query(sqlClause)
	}
	logs.Debugf("SQL语句:%s", sqlClause)
	logs.Debugf("SQL args:%s", tableName)
	pagedList := PagedTableList{}
	pagedList.Code = 0
	pagedList.Msg = core.SUCCESS_MSG
	pagedList.Data = make([]Tableinfo, 0)
	if err != nil {
		logs.Errorf("查询数据发送错误2:%v", err)
		pagedList.Code = core.ERROR
		pagedList.Msg = fmt.Sprintf("数据查询错误： %s", err)
		return pagedList
	}
	for rows.Next() {
		table := Tableinfo{}
		rows.Scan(&table.TableName, &table.Engine, &table.TableComment)
		pagedList.Data = append(pagedList.Data, table)
	}
	sqlClause = "select count(*) from information_schema.tables where table_schema = (select database())"
	if len(tableName) > 0 {
		sqlClause = fmt.Sprintf("%s and table_name like ? ", sqlClause)
	}
	if hasArgs {
		rows, err = tempDb.Query(sqlClause, tableName)
	} else {
		rows, err = tempDb.Query(sqlClause)
	}
	if rows.Next() {
		rows.Scan(&pagedList.Count)
	} else {
		pagedList.Count = 0
	}
	return pagedList
}

func (this *GencodeController) processColumnInfo(dburl, tableName string, group *sync.WaitGroup) {
	defer group.Done()
	if len(dburl) == 0 || len(tableName) == 0 {
		logs.Error("数据库连接字符串或表名为空")
		return
	}
	tempDb, _ := sql.Open("mysql", dburl)
	defer tempDb.Close()
	sqlClause := `select column_name columnName, data_type dataType, column_comment columnComment, column_key columnKey, extra,CHARACTER_MAXIMUM_LENGTH,COLUMN_DEFAULT from information_schema.columns
 			where table_name = ? and table_schema = (select database()) order by ordinal_position`
	rows, err := tempDb.Query(sqlClause, tableName)

	if err != nil {
		logs.Errorf("查询数据发送错误2:%v", err)
		return
	}
	var columnList = make([]Columninfo, 0)
	var keyColumnList = make([]Columninfo, 0)
	for rows.Next() {
		columnInfo := Columninfo{}
		rows.Scan(&columnInfo.ColumnName, &columnInfo.DataType, &columnInfo.ColumnComment, &columnInfo.ColumnKey, &columnInfo.Extra, &columnInfo.Length, &columnInfo.DefaultValue)
		columnInfo.PascalName = utils.ToPascalName(columnInfo.ColumnName)
		columnInfo.CamelName = utils.ToCamelName(columnInfo.ColumnName)
		columnInfo.Lowername = utils.ToLowername(columnInfo.ColumnName)
		runlist := []rune(columnInfo.ColumnComment)
		if length := len(runlist); length == 0 {
			columnInfo.ColumnComment = columnInfo.ColumnName
		} else if length > 16 {
			columnInfo.ColumnComment = string(runlist[:16])
		}
		sqlDataType := strings.ToLower(columnInfo.DataType)
		columnInfo.DataType = sqlDataType
		columnInfo.SimpleJavaType=getJavaSimpleType(sqlDataType)
		length := columnInfo.Length
		if strings.EqualFold(sqlDataType, "tinytext") || (strings.EqualFold(sqlDataType, "varchar") && length > 255 && length < 1000) {
			columnInfo.IsMultiLines = true
			columnInfo.IsString = true
		} else if (strings.EqualFold(sqlDataType, "varchar") && length >= 1000) || strings.Contains(sqlDataType, "text") {
			columnInfo.IsRichText = true
			columnInfo.IsString = true
		} else if strings.Contains(sqlDataType, "char") {
			columnInfo.IsString = true
		} else if strings.Contains(sqlDataType, "blob") {
			columnInfo.IsRichText = true
			columnInfo.IsString = true
			columnInfo.IsBlob = true
		} else if strings.EqualFold(sqlDataType, "bit") {
			columnInfo.IsBool = true
		}
		if strings.Compare(columnInfo.ColumnKey, "PRI") == 0 {
			columnInfo.IsPrimary = true
		}
		if strings.Contains(columnInfo.Extra, "auto_increment") {
			columnInfo.IsAutoincr = true
		}
		columnList = append(columnList, columnInfo)
		if columnInfo.IsPrimary{
			keyColumnList= append(keyColumnList,columnInfo)
		}
	}
	table := Tableinfo{}
	sqlClause = `select table_name tableName, engine, table_comment tableComment, create_time createTime from information_schema.tables 
			where table_schema = (select database()) and table_name = ?`
	rows, err = tempDb.Query(sqlClause, tableName)
	if err != nil {
		logs.Errorf("查询数据发送错误2:%v", err)
		return
	}
	var createTime string
	for rows.Next() {
		rows.Scan(&table.TableName, &table.Engine, &table.TableComment, &createTime)
	}
	logs.Debugf("表名：%s 字段列表", tableName, columnList)
	genByTemplate(&table, &columnList,&keyColumnList)
}

func getJavaSimpleType(sqlType string) string  {
	javaType :="String";
	switch sqlType {
		case "tinyint","int": javaType="Interger"
		case "bigint": javaType="Long"
		case "bit": javaType="Boolean"
		case "time","date","datetime","timestamp": javaType="Date"
		case "decimal": javaType="BigDecimal"
		case "double","float": javaType="BigDecimal"
		case "text","longtext": javaType="String"
	}
	return javaType;
}

type Tableinfo struct {
	TableName    string `json:"tableName"`
	Engine       string `json:"engine"`
	TableComment string `json:"tableComment"`
}



type processRequest struct {
	List []string `json:"list"`
}

/**
mysql DataType:
tinyint
int
bigint

bit

time      时间 '12:25:36'
date      日期 '2008-12-2'
datetime  日期时间 '2008-12-2 22:06:44'
timestamp 自动存储记录修改时间

decimal
double
float

char     最大255，在保存的时候，后面（右边）会用空格填充到指定的长度，在检索的时候后面的空格会去掉。所以检索出来的数据需要再用什么trim之类的函数去处理。(与sql server可能有些不同)
varchar  最大65535 在保存的时候，不进行填充。当值保存和检索时尾部的空格仍保留。
tinytext 最大255
text     最大65535  列不能有默认值，存储或检索过程中，不存在大小写转换.
longtext

blob
longblob

_BLOB和_text存储方式不同，_TEXT以文本方式存储，英文存储区分大小写，而_Blob是以二进制方式存储，不分大小写。
_TEXT可以指定字符集，_BLO不用指定字符集。
*/
type Columninfo struct {
	ColumnName    string //
	DataType      string //
	SimpleJavaType      string //
	ColumnComment string
	ColumnKey     string // 值有4中 ' '，'PRI'，'UNI'，'MUL'。取值顺序(该字段同时有几部分含义)：PRI（主键） > UNI（唯一索引键） > MUI （可重复索引键）
	Extra         string // 可以取值 auto_increment，等其它信息
	Length        int
	DefaultValue  string // 默认值，对应字段：default
	PascalName    string
	CamelName     string
	Lowername     string
	IsMultiLines  bool //是否多行文本，true: 类型 tinytext、varchar（length>255）
	IsRichText    bool //富文本 true：text、text、longtext
	IsBlob        bool // 包含Blob的字段
	IsBool        bool //bit 类型
	IsString      bool //char、varchar、tinytext\text\longtext\blob
	IsPrimary     bool //是否主键
	IsAutoincr    bool
}

type PagedTableList struct {
	api.PagedResult
	Data []Tableinfo `json:"data"`
}
